//
//  CMLOE.m
//  CocoaMesh
//
//  Created by George  Armah on 4/28/09.
//  Copyright 2009 Lafayette College. All rights reserved.
//

#import "CMLOE.h"
#import "CMCocoaMesh.h"
#import "CMLOE+CommonActions.h"

@interface CMLOE (PrivateMethods)

-(id)syncFetchResource:(NSString*)resourceType error:(NSError**)err;
-(BOOL) checkForAuthToken:(NSError **)err;
-(NSURL*) retrieveURLFromResource:(CMResource*)resource withKey:(NSString*)key error:(NSError**)err;
-(CMResource*)createResource:(NSURL*)url error:(NSError**)err;
-(NSArray*)loadMeshResources:(NSURL*)url error:(NSError**)err;
-(CMResource*)loadMeshResource:(NSString*)urlString key:(NSString*)key forValue:(NSString*)value error:(NSError**)err;

@end





@implementation CMLOE

#pragma mark PrivateMethods

-(BOOL) checkForAuthToken:(NSError **)err
{
	if ( [self.authToken isEqualToString:@""] || !self.authToken )
	{
		if ( err )
		{
			*err = [NSError errorWithDomain:CocoaMeshErrorDomain 
									   code:CMERRORMESHDICTABSENT
								   userInfo:[NSDictionary dictionaryWithObjectsAndKeys:NSLocalizedString(@"Top level Mesh dictionary not found",@""), NSLocalizedDescriptionKey,nil]];
		}
		return NO;
	}
	return YES;
}


-(NSURL*) retrieveURLFromResource:(CMResource*)resource withKey:(NSString*)key error:(NSError**)err
{
	NSString * urlString = nil;
	NSString * relativeLink = nil;
	relativeLink = [resource valueForKey:key];
	if ( relativeLink == nil )
	{
		if ( err )
		{
			NSString * errorMessage = [NSString stringWithFormat:@" %@ key value not found", key];
			*err = [NSError errorWithDomain:CocoaMeshErrorDomain 
									   code:CMERRORSELFLINKABSENT 
								   userInfo:[NSDictionary 
											 dictionaryWithObjectsAndKeys:
											 NSLocalizedString(errorMessage,@""), NSLocalizedDescriptionKey,nil]];
		}
		return nil;
	}
	urlString = [[self.topLevelMeshDictionary valueForKey:BASEURIKEY] stringByAppendingString:relativeLink];
	
	return [NSURL URLWithString:urlString];
}

-(CMResource*)createResource:(NSURL*)url error:(NSError**)err
{
	//The very minimum we need to populate the body of this message with ... we can add params to add more later
	//convert resource collection to json
	NSError * convertErr = nil;
	NSDictionary * jsonDict = [NSDictionary dictionaryWithObjectsAndKeys:[self.topLevelMeshDictionary valueForKey:BASEURIKEY], BASEURIKEY, nil];
	NSString * json = [CMDataObjectConverter convertObjectToJSON:jsonDict error:&convertErr];
	if ( convertErr )
	{
		if ( err )
			*err = convertErr;
		return nil;
	}

	//create request
	NSURLRequest * request = [CMRequestConstructor meshResourcePOSTRequest:self.authToken 
																   withURL:url 
																  HTTPBody:[json dataUsingEncoding:NSUTF8StringEncoding]];
	//send request ... should the response be used?
	NSHTTPURLResponse * meshResponse = nil;
	NSError * meshError = nil;
	NSData * responseData = [CMNetworkConnector sendSynchronousRequest:request 
													 returningResponse:&meshResponse 
																 error:&meshError];
	if ( meshError )
	{
		if ( err )
			*err = meshError;
		return nil;
	}
	
	
	//convert response to appropriate cocoa object. Need to figure out which encoding to use
	//Assuming that responseString is fine if there is no meshError. Might not be a valid assumption.
	NSString * responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	
	
	//Convert responseString to dictionary 
	NSError * convertionErr = nil;
	NSDictionary * meshDict = [CMDataObjectConverter convertJSONToObject:responseString error:&convertionErr];
	if ( convertionErr )
	{
		if ( err )
			*err = convertionErr;
		[responseString release];
		return nil;
	}
	[responseString release];
	
	return [[[CMResource alloc] initWithDictionary:meshDict] autorelease];
}

-(NSArray*)loadMeshResources:(NSURL*)url error:(NSError**)err
{
	if ( ! [self checkForAuthToken:err] )
		return nil;

	//create request
	NSURLRequest * request = [CMRequestConstructor meshResourceGETRequest:self.authToken 
																  withURL:url];
	
	
	//send request ... should the response be used?
	NSHTTPURLResponse * meshResponse = nil;
	NSError * meshError = nil;
	NSData * responseData = [CMNetworkConnector sendSynchronousRequest:request 
													 returningResponse:&meshResponse 
																 error:&meshError];
	if ( meshError )
	{
		if ( err )
			*err = meshError;
		return nil;
	}
	
	
	//convert response to appropriate cocoa object. Need to figure out which encoding to use
	NSString * responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	NSArray * meshObjects = [[CMDataObjectConverter convertJSONToObject:responseString error:&meshError] valueForKey:ENTRIESKEY];
	if ( meshError )
	{
		if ( err )
			*err = meshError;
		[responseString release];
		return nil;
	}
	[responseString release];
	
	
	//create, populate and return meshobjects array. 
	//Autorelease because method caller is responsible for retaining
	NSMutableArray * returnArray = [[[NSMutableArray alloc] initWithCapacity:[meshObjects count]] autorelease];
	for (id meshO in meshObjects)
	{
		CMResource * m = [[CMResource alloc] initWithDictionary:(NSDictionary*)meshO];
		[returnArray addObject:m];
		[m release];
	}

	return returnArray;
}


-(CMResource*)loadMeshResource:(NSString*)urlString key:(NSString*)key forValue:(NSString*)value error:(NSError**)err
{
	NSString * exp = [NSString stringWithFormat:CMFILTEREXPRESSIONFORMAT, key, CMEQ, value];
	NSString * formatExp = [NSString stringWithFormat:CMFILTERFORMAT, exp];
	NSString * fullURLString = [[NSString stringWithFormat:@"%@%@", urlString, formatExp] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	
	
	//create request
	NSURLRequest * request = [CMRequestConstructor meshResourceGETRequest:self.authToken 
																  withURL:[NSURL URLWithString:fullURLString]];
	
	
	//send request ... should the response be used?
	//I HATE HAVING TO REPEAT CODE >_< MORE REFACTORING TO BE DONE LATER
	NSHTTPURLResponse * meshResponse = nil;
	NSError * meshError = nil;
	NSData * responseData = [CMNetworkConnector sendSynchronousRequest:request 
													 returningResponse:&meshResponse 
																 error:&meshError];
	if ( meshError )
	{
		if ( err )
			*err = meshError;
		return nil;
	}
	
	
	//convert response to appropriate cocoa object. Need to figure out which encoding to use
	NSString * responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	
	
	NSArray* meshObjects = [[CMDataObjectConverter convertJSONToObject:responseString error:&meshError] valueForKey:ENTRIESKEY];

	if ( meshError )
	{
		if ( err )
			*err = meshError;
		[responseString release];
		return nil;
	}
	[responseString release];
	
	
	return [[[CMResource alloc] initWithDictionary:[meshObjects objectAtIndex:0]] autorelease];
}


#pragma mark PublicMethods

@synthesize authToken;
@synthesize topLevelMeshDictionary;

-(id) init 
{
	if (self = [super init]) 
	{
		authToken = [[NSString alloc] init];
		topLevelMeshDictionary = [[NSMutableDictionary alloc] initWithCapacity:6];
	}
	return self;
}


-(void) dealloc 
{
	[topLevelMeshDictionary release];
	[authToken release];
	[super dealloc];
}



//Most likely code in this method will be refactored since it is so long
-(BOOL)connectWithUserID:(NSString*)userID 
				password:(NSString*)uPassword 
		   applicationID:(NSString*)appID
		 connectionError:(NSError**)err
{
	
	NSURLRequest * wlxRequest = [CMRequestConstructor windowsLiveIDRequest:userID password:uPassword applicationID:appID];
	NSError * wlxError = nil;
	NSHTTPURLResponse * wlxResponse = nil;
	NSData * wlxResponseBody = [CMNetworkConnector sendSynchronousRequest:wlxRequest returningResponse:&wlxResponse error:&wlxError];
	if ( wlxError )
	{
		if ( err )
			*err = wlxError;
		return NO;
	}
		
	
	//Parse auth Token
	NSError * parseError = nil;
	CMXMLParser * parser = [[CMXMLParser alloc] init];
	NSString * authT = [parser parseXML:wlxResponseBody forElementContents:BINARYSECURITYTOKEN parseError:&parseError];
	[parser release];
	if ( parseError )
	{
		if ( err )
			*err = parseError;
		[authT release];
		return NO;
	}	
	if ( [authT isEqualToString:@""] )
	{
		if ( err ) 
		{
			NSDictionary * errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
										NSLocalizedString(@"No authentication token found", @""), NSLocalizedDescriptionKey,
										nil];
			//Using the HTTP status code as error code .... Is this a good idea?
			*err = [NSError errorWithDomain:CocoaMeshErrorDomain code:[wlxResponse statusCode] userInfo:errorDict];
		}		
		[authT release];
		return NO;
	}
	
	self.authToken = authT;	
	[authT release];
	
	
	//Connect to CLOE and get top level dictionary
	NSURLRequest * topLevelCLOERequest = [CMRequestConstructor CLOETopLevelRequest:self.authToken];
	NSError * cloeError = nil;
	NSHTTPURLResponse * cloeResponse = nil;
	NSData * cloeResponseBody = [CMNetworkConnector sendSynchronousRequest:topLevelCLOERequest 
														 returningResponse:&cloeResponse 
																	 error:&cloeError];
	if ( cloeError )
	{
		if ( err )
			*err = cloeError;
		return NO;
	}
	
	
	//We need to convert the response data into a string ... is there a way to obtain encoding data from
	//an NSData object? Assume UTF8 encoding for now :( and use that 
	NSString * json = [[NSString alloc] initWithData:cloeResponseBody encoding:NSUTF8StringEncoding];
	NSDictionary * dict = (NSDictionary *) [CMDataObjectConverter convertJSONToObject:json error:&cloeError];
	if ( cloeError )
	{
		if ( err )
			*err = cloeError;
		[json release];
		return NO;
	}
	self.topLevelMeshDictionary = [NSMutableDictionary dictionaryWithDictionary:dict];
	[json release];
	
	
	//NSLog([NSString stringWithFormat:@"Top Level Mesh Dictioanry is %@", self.topLevelMeshDictionary]);
	return YES;
}





-(NSArray*)loadMeshObjects:(NSError**)err
{	
	//create url
	NSString * meshObjectsURLString = [[self.topLevelMeshDictionary valueForKey:BASEURIKEY] stringByAppendingString:MESHOBJECTS];
	NSURL * meshObjectsURL = [NSURL URLWithString:meshObjectsURLString];
	
	NSArray * resourcesArray = [self loadMeshResources:meshObjectsURL error:err];
	if ( err )
	{
		if ( *err || !resourcesArray )
			return nil;
	}
	
	NSMutableArray * meshObjectsArray = [NSMutableArray arrayWithCapacity:[resourcesArray count]];
	for (CMResource * meshO in  resourcesArray)
	{
		CMMeshObject * m = [[CMMeshObject alloc] initWithResource:meshO];
		[meshObjectsArray addObject:m];
		[m release];
	}
	
	return meshObjectsArray;
}


-(CMMeshObject*)loadMeshObjectWithKey:(NSString*)key forValue:(NSString*)value error:(NSError**)err
{
	NSString * meshObjectsURLString = [[self.topLevelMeshDictionary valueForKey:BASEURIKEY] stringByAppendingString:MESHOBJECTS];
	CMResource * m = [self loadMeshResource:meshObjectsURLString key:key forValue:value error:err];
	
	if ( err )
	{
		if ( *err || m == nil )
			return nil;
	}
	
	return [[[CMMeshObject alloc] initWithResource:m] autorelease];
}


-(BOOL)updateResource:(CMResource*)resource error:(NSError**)err
{
	if ( ! [self checkForAuthToken:err] )
		return NO;
	
	
	//get and save url
	NSURL * meshResourceURL = nil; 
	meshResourceURL = [self retrieveURLFromResource:resource withKey:SELFLINKKEY error:err];
	if ( meshResourceURL == nil )
		return NO;

	
	//convert resource collection to json
	NSError * convertErr = nil;
	NSDictionary * jsonDict = [resource convertToCollection];
	[jsonDict retain];
	NSString * json = [CMDataObjectConverter convertObjectToJSON:jsonDict error:&convertErr];
	[jsonDict release];
	if ( convertErr )
	{
		if ( err )
			*err = convertErr;
		return NO;
	}
	
	
	//create PUT request
	NSURLRequest * request = [CMRequestConstructor meshResourcePUTRequest:self.authToken 
																  withURL:meshResourceURL 
																 HTTPBody:[json dataUsingEncoding:NSUTF8StringEncoding]];
	
	
	//send request ... should the response be used?
	NSHTTPURLResponse * meshResponse = nil;
	NSError * meshError = nil;
	[CMNetworkConnector sendSynchronousRequest:request 
							 returningResponse:&meshResponse 
										 error:&meshError];
	if ( meshError )
	{
		if ( err )
			*err = meshError;
		return NO;
	}
	
	return YES;
}




-(BOOL)deleteResource:(CMResource*)resource error:(NSError**)err
{
	if ( ! [self checkForAuthToken:err] )
		return NO;
	
	
	//get and save selflink
	NSURL * meshResourceURL = nil;
	meshResourceURL = [self retrieveURLFromResource:resource withKey:SELFLINKKEY error:err];
	if ( meshResourceURL == nil )
		return NO;

	
	//create DELETE request
	NSURLRequest * request = [CMRequestConstructor meshResourcePUTRequest:self.authToken 
																  withURL:meshResourceURL
																 HTTPBody:nil];
	
	//send request ... should the response be used?
	NSHTTPURLResponse * meshResponse = nil;
	NSError * meshError = nil;
	[CMNetworkConnector sendSynchronousRequest:request 
							 returningResponse:&meshResponse 
										 error:&meshError];
	if ( meshError )
	{
		if ( err )
			*err = meshError;
		return NO;
	}
	
	return YES;
}




-(CMMeshObject*)createMeshObject:(NSError**)err
{
	
	if ( ! [self checkForAuthToken:err] )
		return nil;

	
	//create url
	NSString * meshObjectsURLString = [[self.topLevelMeshDictionary valueForKey:BASEURIKEY] stringByAppendingString:MESHOBJECTS];
	NSURL * meshObjectsURL = [NSURL URLWithString:meshObjectsURLString];
	
	
	//Create Meshobject
	CMResource * resource = [self createResource:meshObjectsURL error:err];
	if ( err )
	{
		if ( *err )
		{
			return nil;
		}
	}
	
	//Get the Datafeeds URL
	NSString * datafeedsString = [resource valueForKey:DATAFEEDSLINKKEY];
	if ( ! datafeedsString ) 
		return nil;
	NSURL * datafeedsURL = [NSURL URLWithString:[[self.topLevelMeshDictionary valueForKey:BASEURIKEY] 
												 stringByAppendingString:datafeedsString]];
	
	//Create a dataFeed for this mesh Object
	[self createResource:datafeedsURL error:err];
	if ( err )
	{
		if ( *err )
		{
			return nil;
		}
	}
	
	return [[[CMMeshObject alloc] initWithResource:resource] autorelease];
}
@end
